package com.api.log;

import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.api.config.RedisService;
import com.api.config.StartupTimeRecorder;
import com.api.entity.SysLog;
import com.api.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

import static com.api.entity.table.SysConfigTableDef.SYS_CONFIG;
import static com.api.entity.table.UserTableDef.USER;

/**
 * @Description: 描述
 * @Author: yangsf
 * @CreateDate: 2023/7/24
 */

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Value("${sa-token.token-name}")
    public String tokenName;
    @Autowired
    RedisService redisService;

    //两个..代表所有子目录，最后括号里的两个..代表所有参数
    @Pointcut("execution( * com.api.controller.*.*(..))")
    public void logPointCut() {
    }


    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {

    }

    @AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(Object ret) {

    }

    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);

        String logValue = null;
        if (logAnnotation != null) {
            logValue = logAnnotation.value();
            log.info("请求的接口 : " + logValue);
        }
        // 记录下请求内容
        log.info("请求地址 : " + request.getRequestURL().toString());
        //log.info("方法描述 : " + );
        log.info("HTTP METHOD : " + request.getMethod());
        // 获取真实的ip地址
        String clientIP = ServletUtil.getClientIP(request);
        log.info("IP : "+ clientIP);
        String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        log.info("CLASS_METHOD : " + classMethod);
        String requestArgs = null;

        Object[] args = joinPoint.getArgs();
        String[] parameterNames = signature.getParameterNames();

        Map<String, Object> parameters = new HashMap<>();
        for (int i = 0; i < parameterNames.length; i++) {
            if (args[i] instanceof MultipartFile[]) {
                MultipartFile[] files = (MultipartFile[]) args[i];
                // 特殊处理文件数组类型
                // 可以记录文件数量或对每个文件做进一步处理
                parameters.put(parameterNames[i], "包含文件个数: " + files.length);
            } else {
                parameters.put(parameterNames[i], args[i]);
            }
        }
        if(!parameters.isEmpty()){
            requestArgs = JSONUtil.parseObj(parameters).toStringPretty();
        }
        // 打印或记录参数信息
//        parameters.forEach((name, value) -> System.out.println(name + ": " + value));

//        log.info("参数 : " + Arrays.toString(args));
//        requestArgs = Arrays.toString(args);
//        if(null != args && args.length>0){
//            boolean isMultipart = ServletFileUpload.isMultipartContent(request);
//            if (!isMultipart){
//                log.info("参数详情 : " + JSON.toJSONString(args[0]));
//                requestArgs = JSON.toJSONString(args[0]);
//                System.out.println("参数详情 : " + requestArgs);
//            }
//        }
        String token = request.getHeader(tokenName);
        log.info("请求Token : " + token);

        User operUser = null;
        String loginUserName = null;
        log.info("请求方法名称 : " + method.getName());
        if(method.getName().equals("doLogin")){
            HashMap bean = JSONUtil.toBean(requestArgs, HashMap.class);
            if(null != bean){
                loginUserName = null==bean.get("username")?"":bean.get("username").toString();
            }
        }
        else {
            if(StrUtil.isNotEmpty(token)){
                operUser = User.create().select(USER.ALL_COLUMNS).where(USER.TOKEN.eq(token)).one();
                if(null==operUser){
                    throw new NotLoginException("请登录后再访问","","-1");
                }
            }
        }

        //记录执行开始时间
        long startTime = System.currentTimeMillis();
        // ob 为方法的返回值
        Object ob = StartupTimeRecorder.getProceed(joinPoint);
        String resJson= JSONUtil.parseObj(ob).toStringPretty();
        long takeTime = System.currentTimeMillis() - startTime;
        log.info("返回参数ob : " + resJson);
        log.info("耗时 : " + takeTime + "毫秒");
        saveLogs(operUser, loginUserName, logValue, classMethod, clientIP, requestArgs, resJson, takeTime);
        return ob;
    }


    @Async
    public void saveLogs(User operUser, String loginUserName, String logValue, String classMethod, String clientIP, String requestArgs, String resJson, Long takeTime){
        if(null == operUser){
            SysLog.create()
                    .setLoginName(loginUserName)
                    .setUserName(loginUserName)
                    .setLogValue(logValue)
                    .setClassMethod(classMethod)
                    .setOperIp(clientIP)
                    .setRequestArgs(requestArgs)
                    .setResArg(resJson)
                    .setTakeTime(takeTime)
                    .save();
        }
        else{
            SysLog.create()
                    .setOperId(operUser.getId())
                    .setLoginName(operUser.getLoginName())
                    .setUserName(operUser.getUserName())
                    .setLogValue(logValue)
                    .setClassMethod(classMethod)
                    .setOperIp(clientIP)
                    .setRequestArgs(requestArgs)
                    .setResArg(resJson)
                    .setTakeTime(takeTime)
                    .save();
        }

    }


}
